package vg.services.user_interface_manager.additional_swing_components.conditionPanel;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

import vg.services.user_interface_manager.additional_swing_components.conditionPanel.components.ConditionWrapper;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.components.GeneralCondition;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.components.Operator;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.data.AConditionData;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.data.ConditionalPanelData;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.data.GeneralConditionData;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.data.OperatorData;

public class ConditionalPanel {
	// Defines
	public static final int DEF_VERTEX_TYPE = 1;
	public static final int DEF_EDGE_TYPE = 2;
	// Swing components
	private final JPanel view;
	private JPanel conditionalPanel;
	private JScrollPane conditionalPanelScroll; 
	private JButton plusButton;
	private List<ConditionWrapper> conditions;
	private List<Operator> operators;
	// Data
	private Map<String, Set<String>> generalAttributes = null;
	private Set<String> numberAttributes = null;
	// Mutex
	private final Object theMutexObject = new Object(); 
	/**
	 * Constructor.
	 */
	public ConditionalPanel() {
		// clear and initialize
		clear_and_init();
		// package of interface
		this.view = new JPanel(new GridLayout(1, 1));
		this.conditionalPanel = new JPanel(new GridBagLayout());
		this.conditionalPanelScroll = new JScrollPane(this.conditionalPanel);
		this.view.add(this.conditionalPanelScroll);
		//build plus conditional button
		this.plusButton = new JButton("add new condition");
		this.plusButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				add_new_condition();
			}
		});
	}
	
	/**
	 * This method clears existing conditions and sets collection of attributes for conditions panel.
	 * @param generalAttributes - is map, where key is name of attribute and value is array of values of this attribute.
	 * @param numberAttributes - is set, which contains name of number attributes only.
	 */
	public void setAttributes(Map<String, Set<String>>generalAttributes, Set<String> numberAttributes) {
		synchronized (theMutexObject) {
			if(generalAttributes == null)
				this.generalAttributes = Collections.emptyMap();
			else	
				this.generalAttributes = generalAttributes;
			if(numberAttributes == null)
				this.numberAttributes = Collections.emptySet();
			else
				this.numberAttributes = numberAttributes;
			clear_and_init();
			if(SwingUtilities.isEventDispatchThread()) {
				update();
			} else {
				SwingUtilities.invokeLater(new Runnable() {
					@Override
					public void run() {
						synchronized (theMutexObject) {
							update();
						}
					}
				});
			}		
		}
	}
	
	/**
	 * This method updates UI theme.
	 */
	public void updateUITheme() {
		synchronized (theMutexObject) {
			if(SwingUtilities.isEventDispatchThread()) {
				update();
			} else {
				SwingUtilities.invokeLater(new Runnable() {
					@Override
					public void run() {
						synchronized (theMutexObject) {
							update();
						}
					}
				});
			}
		}
	}
//=============================================================================
//------------Get Methods------------------------------------------------------
	/**
	 * Warning: don't modify this map, only reading.
	 */
	public Map<String, Set<String>> getGeneralAttributes() {
		synchronized (theMutexObject) {
			return generalAttributes;
		}
	}
	
	/**
	 * Warning: don't modify this set, only reading.
	 */
	public Set<String> getNumberAttributes() {
		return numberAttributes;
	}
	
	/**
	 * This method returns view of this component.
	 */
	public JPanel getView() {
		return view;
	}
	
	public ConditionalPanelData getConditionPanelData() {
		synchronized (theMutexObject) {
			ConditionalPanelData data = new ConditionalPanelData();
			data.conditions = getConditions();
			data.operators = getOperators();
			return data;			
		}
	}
//=============================================================================
//---------------PRIVATE METHODS-----------------------------------------------	
	private List<AConditionData> getConditions() {
		List<AConditionData> cond_data = new ArrayList<AConditionData>();
		for(ConditionWrapper buf : conditions) {
			switch(buf.getType()) {
				case ConditionWrapper.DEF_GENERAL_CONDITION: {
					GeneralConditionData e = new GeneralConditionData(AConditionData.DEF_GENERAL_CONDITION);
					GeneralCondition gc = (GeneralCondition)buf.getCondition();
					e.name = gc.getName();
					e.left_bracket = gc.getLeftBracket();
					e.right_bracket = gc.getRightBracket();
					e.value = gc.getValue();
					cond_data.add(e);
					break;
				}
			}
		}
		return cond_data;
	}
	
	private List<OperatorData> getOperators() {
		List<OperatorData> oper_data = new ArrayList<OperatorData>();
		int i = 0;
		for(Operator buf : operators) {
			if(i < operators.size() - 1) {
				OperatorData e = new OperatorData();
				e.operator = buf.getText();
				oper_data.add(e);
			}
			i++;
		}
		return oper_data;
	}
	
	private void clear_and_init() {
		conditions = new ArrayList<ConditionWrapper>();
		operators = new ArrayList<Operator>();
	}
	
	private void add_new_condition() {
		final ConditionWrapper cw = new ConditionWrapper();
		final Operator op = new Operator();
		
		cw.setGeneralType(generalAttributes);
		cw.addClosingListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				conditions.remove(cw);
				operators.remove(op);
				update();
			}
		});
		conditions.add(cw);
		operators.add(op);
		update();
	}
	/**
	 * This method updates conditional panel
	 */
	private void update() {
		//clear panel
		ConditionalPanel.this.conditionalPanel.removeAll();
		//create gbc parameter
		GridBagConstraints gbc = null;
		//add all conditions to panel
		for(int i = 0; i < conditions.size(); i++) {
			// add condition
			gbc = new GridBagConstraints(0,2*i,  1,1,  1,0,  GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(3, 3, 3, 0),  0,0);
			conditionalPanel.add(conditions.get(i).getView(), gbc);
		}
		for(int i = 0; i < operators.size() - 1; i++) {
			gbc = new GridBagConstraints(0,2*i + 1,  5,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(3, 3, 3, 3),  0,0);
			conditionalPanel.add(operators.get(i).getView(), gbc);
		}
		//add plus button to panel 
		gbc = new GridBagConstraints(0,2*conditions.size() + 1,  5,1,  1,1,  GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0);
		conditionalPanel.add(plusButton, gbc);
		// update ui
		view.updateUI();
	}
}
